<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Demo</title>

    <link href="https://cdn.jsdelivr.net/npm/@quasar/extras/material-icons/material-icons.css" rel="stylesheet">
    <link href="https://unpkg.com/quasar@2/dist/quasar.prod.css" rel="stylesheet">
</head>

<body>
    <div id="q-app">
        <q-layout>
            <q-tabs v-model="tab" class="bg-primary text-white">
                <q-tab name="config" label="配置"></q-tab>
                <q-tab name="run" label="运行"></q-tab>
            </q-tabs>

            <q-tab-panels class="q-pa-md" v-model="tab" animated>
                <!-- 配置面板 -->
                <q-tab-panel name="config" class="q-pa-md">
                    <div class="q-gutter-md">
                        <div class="row q-col-gutter-md">
                            <!-- 编译服务器配置 -->
                            <div class="col-6">
                                <div class="text-h6 q-mb-md">编译服务器</div>
                                <q-input filled v-model="cfgBuild.ip" label="IP" class="q-mb-sm"></q-input>
                                <q-input filled v-model.number="cfgBuild.port" type="number" label="端口"
                                    class="q-mb-sm"></q-input>
                                <q-input filled v-model="cfgBuild.user" label="用户" class="q-mb-sm"></q-input>
                                <q-input filled v-model="cfgBuild.pwd" type="password" label="密码（本地明文保存~）"
                                    class="q-mb-sm"></q-input>

                                <q-btn color="primary" @click="testConn('build')" class="q-mt-sm">测试连接</q-btn>
                                <q-separator spaced class="q-my-md"></q-separator>
                                <!-- 添加代码来源选项 -->
                                <q-option-group v-model="cfgBuild.codeSource" :options="[
                                        { label: '编译服务器特定路径有代码', value: 'remote' },
                                        { label: '本地下载上传到编译服务器', value: 'local' }
                                    ]" color="primary" inline class="q-mb-sm"></q-option-group>

                                <!-- 当选择远程代码时，显示路径配置 -->
                                <q-input v-if="cfgBuild.codeSource === 'remote'" filled v-model="cfgBuild.codePath"
                                    label="代码路径" class="q-mb-sm"></q-input>

                                <!-- 当选择本地代码时，显示Git仓库和分支配置 -->
                                <div v-if="cfgBuild.codeSource === 'local'">
                                    <q-input filled v-model="cfgBuild.gitRepo" label="Git仓库地址"
                                        class="q-mb-sm"></q-input>
                                    <q-input filled v-model="cfgBuild.gitBranch" label="Git分支"
                                        class="q-mb-sm"></q-input>
                                </div>
                                <q-separator spaced class="q-my-md"></q-separator>

                                <div class="text-h6 q-mb-md">运行服务器</div>
                                <q-input filled v-model="cfgRun.ip" label="IP" class="q-mb-sm"></q-input>
                                <q-input filled v-model.number="cfgRun.port" type="number" label="端口"
                                    class="q-mb-sm"></q-input>
                                <q-input filled v-model="cfgRun.user" label="用户" class="q-mb-sm"></q-input>
                                <q-input filled v-model="cfgRun.pwd" type="password" label="密码（本地明文保存~）"
                                    class="q-mb-sm"></q-input>
                                <q-btn color="primary" @click="testConn('run')" class="q-mt-sm">测试连接</q-btn>
                            </div>

                            <!-- 运行服务器配置 -->
                            <div class="col-6">

                                <div class="text-h6 q-mb-md">运行配置</div>
                                <q-separator spaced class="q-my-md"></q-separator>

                                <div class="text-h6 q-mb-md"> >>> MindIE版本</div>
                                <q-input filled v-model="cfgRun.versionPrefix" label="版本前缀（cmc搜索使用）"
                                    class="q-mb-sm"></q-input>

                                <q-option-group v-model="cfgRun.mindieSource" :options="[
                                        { label: 'cmc下载', value: 'cmc' },
                                        { label: '就用镜像中的mindie版本', value: 'local' }
                                    ]" color="primary" inline class="q-mb-sm"></q-option-group>

                                <div v-if="cfgRun.mindieSource === 'cmc'">
                                    <div class="q-mb-md row items-center"> <span> 需要配置 artget（存储目录，用户密码） </span>
                                        <q-space></q-space>
                                        <q-btn flat size="sm" @click="reloadMindIEVersion" color="primary">
                                            刷新
                                        </q-btn>
                                        <q-btn flat size="sm" @click="openArtgetConfig">
                                            点击打开配置
                                        </q-btn>
                                    </div>
                                    <!-- 修改：将原来的q-select替换为两个控件 -->
                                    <div class="q-mb-sm">
                                        <q-select
                                            filled
                                            v-model="selectedMindieVersion"
                                            :options="listReleaseSnapshotVersions"
                                            label="MindIE 版本"
                                            :loading="loadingImages"
                                            @update:model-value="addSelectedMindieVersion"
                                            new-value-mode="add-unique"
                                        ></q-select>
                                    </div>
                                    <!-- 使用q-chip标签替代列表 -->
                                    <div class="q-mb-sm">
                                        <q-chip
                                            v-for="(version, index) in cfgRun.mindieRunVersions"
                                            :key="index"
                                            removable
                                            @remove="removeSelectedMindieVersion(index)"
                                            color="teal"
                                            text-color="white"
                                            square
                                            dense
                                        >
                                            {{ version }}
                                        </q-chip>
                                    </div>
                                    <!-- 修改结束 -->
                                </div>

                                <q-separator spaced class="q-my-md"></q-separator>
                                <div class="text-h6 q-mb-md"> >>> 选择镜像</div>
                                <q-option-group v-model="cfgRun.imageSource" :options="[
                                        { label: '下载镜像', value: 'cmc' },
                                        { label: '就用服务器上已有镜像', value: 'local' }
                                    ]" color="primary" inline class="q-mb-sm"></q-option-group>

                                <div v-if="cfgRun.imageSource === 'cmc'">
                                    <div class="q-mb-md row items-center"> <span> 需要配置 artget（存储目录，用户密码） </span>
                                        <q-space></q-space>
                                        <q-btn flat size="sm" @click="reloadMindIEVersion" color="primary">
                                            刷新
                                        </q-btn>
                                        <q-btn flat size="sm" @click="openArtgetConfig">
                                            点击打开配置
                                        </q-btn>
                                    </div>
                                    <q-select filled v-model="cfgRun.imageVersion" :options="listReleaseVersions"
                                        label="MindIE B版本" :loading="loadingImages" class="q-mb-sm"></q-select>
                                </div>
                                <div v-if="cfgRun.imageSource === 'local'">
                                    <q-select filled v-model="cfgRun.imageName" :options="images" label="Docker 镜像"
                                        :loading="loadingImages" class="q-mb-sm"></q-select>
                                </div>
                                
                                <q-separator spaced class="q-my-md"></q-separator>
                                <div class="text-h6 q-mb-md"> >>> 资源下载</div>
       
                                <q-option-group v-model="cfgRun.datasetOption" :options="[
                                    { label: '下载数据集', value: 'download' },
                                    { label: '使用服务器上已有路径', value: 'local' }
                                ]" color="primary" inline class="q-mb-sm"></q-option-group>
                                
                                <q-input v-if="cfgRun.datasetOption === 'local'" filled v-model="cfgRun.datasetPath" label="数据集路径" class="q-mb-sm"></q-input>
                                <q-input v-if="cfgRun.datasetOption === 'download'" filled v-model="cfgRun.datasetDownloadPath" label="数据集下载路径" class="q-mb-sm"></q-input>
      

                            <!-- 模型配置 -->
                                <q-option-group v-model="cfgRun.modelOption" :options="[
                                    { label: '下载模型', value: 'download' },
                                    { label: '使用服务器上已有路径', value: 'local' }
                                ]" color="primary" inline class="q-mb-sm"></q-option-group>
                                
                                <q-input v-if="cfgRun.modelOption === 'local'" filled v-model="cfgRun.modelPath" label="模型路径" class="q-mb-sm"></q-input>
                                <q-input v-if="cfgRun.modelOption === 'download'" filled v-model="cfgRun.modelDownloadPath" label="模型下载路径" class="q-mb-sm"></q-input>
         
                        </div>
                            </div>

                        <!-- 添加保存按钮 -->
                        <q-separator spaced class="q-my-md"></q-separator>

                    </div>
                </q-tab-panel>

                <!-- 运行面板 -->
                <q-tab-panel name="run" class="q-pa-md">
                    <!-- 修改:添加一键运行按钮 -->
                    <div class="q-gutter-sm">
                        <q-btn color="primary" @click="executeBuild" :disable="running" :loading="buildRunning">
                            <template v-slot:loading>
                                <q-spinner-hourglass class="on-left" />
                                编译中...
                            </template>
                            1.编译
                        </q-btn>
                        <q-btn color="secondary" @click="executeRunCommand" :disable="running"
                            :loading="runCommandRunning">
                            <template v-slot:loading>
                                <q-spinner-hourglass class="on-left" />
                                运行中...
                            </template>
                            2.运行
                        </q-btn>
                        <q-btn color="accent" @click="executeAll" :disable="running" :loading="running">
                            <template v-slot:loading>
                                <q-spinner-hourglass class="on-left" />
                                执行中...
                            </template>
                            一键运行
                        </q-btn>
                        <q-btn color="negative" @click="stopRun" v-if="running">停止</q-btn>
                    </div>
                    
                    <!-- 添加GPU卡选择 -->
                    <div class="q-mt-md row items-center q-gutter-sm">
                        <q-btn class="q-mx-sm" color="info" @click="checkGpuStatus" label="查看卡情况"> </q-btn>
                        <div class="row q-gutter-sm">
                            <q-checkbox 
                                v-for="n in 8" 
                                :key="n-1"
                                v-model="selectedGpus" 
                                :val="n-1" 
                                :label="`GPU ${n-1}`"
                                @update:model-value="saveGpuConfig"
                            />
                        </div>
                    </div>
                    
                    <div class="q-mt-md">
                        <div class="row items-center">
                            <div class="text-h6">运行日志</div>
                            <q-space></q-space>
                            <q-btn flat size="sm" @click="toggleAutoScroll" :color="autoScroll ? 'primary' : 'grey'">
                                {{ autoScroll ? '自动滚动(开)' : '自动滚动(关)' }}
                            </q-btn>
                            <q-btn flat size="sm" @click="clearLog" color="warning">
                                清空日志
                            </q-btn>
                        </div>
                        <q-scroll-area ref="logArea" style="height: calc(100vh - 220px); min-height: 400px;"
                            class="bg-grey-2 q-pa-sm">
                            <pre>{{ runLog }}</pre>
                        </q-scroll-area>
                    </div>
                </q-tab-panel>
            </q-tab-panels>

            <!-- 新增: 在 q-page 内添加 sticky 容器 -->
            <q-page-sticky position="bottom-right" :offset="[18, 18]">
                <q-btn 
                    color="teal" 
                    @click="saveConfig" 
                    round 
                    size="lg" 
                    icon="save"
                    shadow-10
                    unelevated
                >
                    <q-tooltip>保存配置</q-tooltip>
                </q-btn>
            </q-page-sticky>
        </q-layout>

<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/quasar@2/dist/quasar.umd.prod.js"></script>

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

<script>
    console.log('Vue', Vue);
    console.log('Quasar', Quasar);
    const { ref, reactive, nextTick } = Vue;

    const app = Vue.createApp({
        setup() {
            const tab = ref('config');

            // 配置
            const cfgBuild = reactive({
                ip: '',
                port: 22,
                user: '',
                pwd: '',
                codeSource: 'remote',  // 添加默认值
                codePath: '',          // 添加代码路径
                gitRepo: '',           // 添加Git仓库地址
                gitBranch: ''          // 添加Git分支
            });
            const cfgRun = reactive({
                ip: '', port: 22, user: '', pwd: '',
                versionPrefix: 'MindIE 2.1',
                mindieSource: 'local', mindieRunVersions: [], imageSource: 'local', imageName: '', imageVersion: '',
                datasetOption: '', modelOption: '', datasetPath: '', modelPath: ''
            });
            const selectedMindieVersion = ref(''); // 添加这行
            const images = ref([]);
            const selectedImage = ref(null);
            const loadingImages = ref(false);
            const listReleaseVersions = ref([]);
            const listReleaseSnapshotVersions = ref([]);
            const selectedGpus = ref([]); // 添加GPU选择数据模型

            // 日志
            const runLog = ref('');
            const running = ref(false);
            const buildRunning = ref(false);
            const runCommandRunning = ref(false);
            const autoScroll = ref(true);
            const logArea = ref(null);
            let socket = null;

            // 页面加载时自动读取配置
            Vue.onMounted(async () => {
                try {
                    const res = await axios.get('/api/config');
                    const config = res.data;

                    if (config.build) {
                        Object.assign(cfgBuild, config.build);
                    }

                    if (config.run) {
                        Object.assign(cfgRun, config.run);
                    }

                    if (config.selectedImage) {
                        selectedImage.value = config.selectedImage;
                    }
                    
                    // 读取GPU配置
                    if (config.selectedGpus) {
                        selectedGpus.value = config.selectedGpus;
                    }
                } catch (e) {
                    Quasar.Notify.create({ type: 'negative', message: '加载配置失败: ' + e.toString() });
                }
            });

            // 测试连接
            async function testConn(type) {
                const cfg = type === 'build' ? cfgBuild : cfgRun;
                try {
                    const res = await axios.post('/api/test', cfg);
                    Quasar.Notify.create({ type: res.data.ok ? 'positive' : 'negative', message: res.data.ok ? '连接成功' : res.data.err });

                    // 连接成功后自动保存配置
                    if (res.data.ok) {
                        await saveConfig();
                    }
                } catch (e) {
                    Quasar.Notify.create({ type: 'negative', message: e.toString() });
                }
            }

            // 当运行服务器配置改变，自动拉镜像列表
            Vue.watch([() => cfgRun.ip, () => cfgRun.port, () => cfgRun.user, () => cfgRun.pwd], async () => {
                if (!cfgRun.ip || !cfgRun.user) return;
                loadingImages.value = true;
                try {
                    const res = await axios.post('/api/images', cfgRun);
                    if (res.data.ok) images.value = res.data.images;
                    else Quasar.Notify.create({ type: 'negative', message: res.data.err });
                } catch (e) {
                    Quasar.Notify.create({ type: 'negative', message: e.toString() });
                } finally {
                    loadingImages.value = false;
                }
            }, { immediate: true });

            // 保存配置功能
            async function saveConfig() {
                try {
                    const config = {
                        build: { ...cfgBuild },
                        run: { ...cfgRun },
                        selectedImage: selectedImage.value,
                        selectedGpus: selectedGpus.value  // 添加GPU配置保存
                    };

                    await axios.post('/api/config', config);
                    Quasar.Notify.create({ type: 'positive', message: '配置已保存' });
                } catch (e) {
                    Quasar.Notify.create({ type: 'negative', message: '保存配置失败: ' + e.toString() });
                }
            }

            async function reloadMindIEVersion() {
                try {
                    loadingImages.value = true;
                    const res = await axios.post('/api/mindieVersions', cfgRun);
                    if (res.data.ok) {
                        listReleaseSnapshotVersions.value = res.data.versions.release_snapshot_versions;
                        listReleaseVersions.value = res.data.versions.release_versions;
                    }
                    else Quasar.Notify.create({ type: 'negative', message: res.data.err });
                } catch (e) {
                    Quasar.Notify.create({ type: 'negative', message: e.toString() });
                } finally {
                    loadingImages.value = false;
                }
            }

            async function openArtgetConfig() {
                try {
                    loadingImages.value = true;
                    const res = await axios.post('/api/openArtgetConfig', cfgRun);
                } catch (e) {
                    Quasar.Notify.create({ type: 'negative', message: e.toString() });
                } finally {
                    loadingImages.value = false;
                }
            }

            function toggleAutoScroll() {
                autoScroll.value = !autoScroll.value;
            }

            // 添加选中的MindIE版本到列表
            function addSelectedMindieVersion(value) {
                if (value && !cfgRun.mindieRunVersions.includes(value)) {
                    cfgRun.mindieRunVersions.push(value);
                }
                selectedMindieVersion.value = '';
            }

            // 加载配置
            async function loadConfig() {
                const res = await axios.get('/api/config');
                return res.data;
            }

            // 从列表中删除选中的MindIE版本
            function removeSelectedMindieVersion(index) {
                cfgRun.mindieRunVersions.splice(index, 1);
            }

            // 添加清空日志功能
            function clearLog() {
                runLog.value = '';
            }

            // 保存GPU配置
            async function saveGpuConfig() {
                try {
                    const config = await loadConfig();
                    config.selectedGpus = selectedGpus.value;
                    await saveConfig(config);
                } catch (e) {
                    Quasar.Notify.create({ type: 'negative', message: '保存GPU配置失败: ' + e.toString() });
                }
            }

            // 查看GPU状态
            async function checkGpuStatus() {
                try {
                    const res = await axios.post('/api/checkGpuStatus', {});
                    if (res.data.ok) {
                        Quasar.Notify.create({ type: 'positive', message: 'GPU状态检查完成' });
                    } else {
                        Quasar.Notify.create({ type: 'negative', message: 'GPU状态检查失败: ' + res.data.err });
                    }
                } catch (e) {
                    Quasar.Notify.create({ type: 'negative', message: 'GPU状态检查失败: ' + e.toString() });
                }
            }

            // 滚动到底部
            function scrollToBottom() {
                nextTick(() => {
                    const area = logArea.value;
                    if (area) {
                        const el = area.getScrollTarget();
                        el.scrollTop = el.scrollHeight;
                    }
                });
            }

            // 新增：执行编译命令
            async function executeBuild() {
                if (running.value) return;
                buildRunning.value = true;
                running.value = true;
                runLog.value += "[编译] 执行中...\n";

                try {
                    socket = new WebSocket(`ws://${location.host}/ws/build`);
                    socket.onopen = () => {
                        // 不再需要发送配置信息，后端从配置文件读取
                    };
                    // 添加执行结果状态变量
                    let buildSuccess = false;
                    socket.onmessage = (ev) => {
                        // 解析JSON格式消息
                        try {
                            const msg = JSON.parse(ev.data);
                            if (msg.type === "msg") {
                                runLog.value += msg.data;
                                if (autoScroll.value) {
                                    scrollToBottom();
                                }
                            } else if (msg.type === "exit") {
                                // 根据退出码判断是否成功
                                buildSuccess = (msg.data === 0);
                                runLog.value += `[编译] 完成，退出码: ${msg.data}\n`;
                            }
                        } catch (e) {
                            // 如果不是JSON格式，按原样处理
                            runLog.value += ev.data;
                            if (autoScroll.value) {
                                scrollToBottom();
                            }
                        }
                    };
                    // 添加返回Promise以便等待执行结果
                    return new Promise((resolve) => {
                        socket.onclose = () => {
                            buildRunning.value = false;
                            running.value = false;
                            // 使用结构化数据判断执行结果
                            if (buildSuccess) {
                                Quasar.Notify.create({ type: 'positive', message: '编译完成' });
                            } else {
                                Quasar.Notify.create({ type: 'negative', message: '编译失败' });
                            }
                            resolve(buildSuccess);
                        };
                    });
                } catch (e) {
                    buildRunning.value = false;
                    running.value = false;
                    Quasar.Notify.create({ type: 'negative', message: '编译失败: ' + e.toString() });
                    return Promise.resolve(false); // 返回失败状态
                }
            }

            // 新增：执行运行命令
            async function executeRunCommand(command = "ls -lh") {
                if (running.value) return;
                runCommandRunning.value = true;
                running.value = true;
                runLog.value += `[运行] 执行命令: ${command}...\n`;

                try {
                    socket = new WebSocket(`ws://${location.host}/ws/run-command`);
                    socket.onopen = () => {
                        // 发送命令到后端
                        socket.send(JSON.stringify({ command }));
                    };
                    // 添加执行结果状态变量
                    let runSuccess = false;
                    socket.onmessage = (ev) => {
                        // 解析JSON格式消息
                        try {
                            const msg = JSON.parse(ev.data);
                            if (msg.type === "msg") {
                                runLog.value += msg.data;
                                if (autoScroll.value) {
                                    scrollToBottom();
                                }
                            } else if (msg.type === "exit") {
                                // 根据退出码判断是否成功
                                runSuccess = (msg.data === 0);
                                runLog.value += `[运行] 完成，退出码: ${msg.data}\n`;
                            }
                        } catch (e) {
                            // 如果不是JSON格式，按原样处理
                            runLog.value += ev.data;
                            if (autoScroll.value) {
                                scrollToBottom();
                            }
                        }
                    };
                    // 添加返回Promise以便等待执行结果
                    return new Promise((resolve) => {
                        socket.onclose = () => {
                            runCommandRunning.value = false;
                            running.value = false;
                            // 使用结构化数据判断执行结果
                            if (runSuccess) {
                                Quasar.Notify.create({ type: 'positive', message: '运行完成' });
                            } else {
                                Quasar.Notify.create({ type: 'negative', message: '运行失败' });
                            }
                            resolve(runSuccess);
                        };
                    });
                } catch (e) {
                    runCommandRunning.value = false;
                    running.value = false;
                    Quasar.Notify.create({ type: 'negative', message: '运行失败: ' + e.toString() });
                    return Promise.resolve(false); // 返回失败状态
                }
            }

            // 新增：执行编译命令
            async function executeCompileCommand(command = "ls -l") {
                if (running.value) return;
                buildRunning.value = true;
                running.value = true;
                runLog.value += `[编译] 执行命令: ${command}...\n`;

                try {
                    socket = new WebSocket(`ws://${location.host}/ws/compile-command`);
                    socket.onopen = () => {
                        // 发送命令到后端
                        socket.send(JSON.stringify({ command }));
                    };
                    // 添加执行结果状态变量
                    let compileSuccess = false;
                    socket.onmessage = (ev) => {
                        // 解析JSON格式消息
                        try {
                            const msg = JSON.parse(ev.data);
                            if (msg.type === "msg") {
                                runLog.value += msg.data;
                                if (autoScroll.value) {
                                    scrollToBottom();
                                }
                            } else if (msg.type === "exit") {
                                // 根据退出码判断是否成功
                                compileSuccess = (msg.data === 0);
                                runLog.value += `[编译] 完成，退出码: ${msg.data}\n`;
                            }
                        } catch (e) {
                            // 如果不是JSON格式，按原样处理
                            runLog.value += ev.data;
                            if (autoScroll.value) {
                                scrollToBottom();
                            }
                        }
                    };
                    // 添加返回Promise以便等待执行结果
                    return new Promise((resolve) => {
                        socket.onclose = () => {
                            buildRunning.value = false;
                            running.value = false;
                            // 使用结构化数据判断执行结果
                            if (compileSuccess) {
                                Quasar.Notify.create({ type: 'positive', message: '编译完成' });
                            } else {
                                Quasar.Notify.create({ type: 'negative', message: '编译失败' });
                            }
                            resolve(compileSuccess);
                        };
                    });
                } catch (e) {
                    buildRunning.value = false;
                    running.value = false;
                    Quasar.Notify.create({ type: 'negative', message: '编译失败: ' + e.toString() });
                    return Promise.resolve(false); // 返回失败状态
                }
            }

            // 新增：执行本地命令
            async function executeLocalCommand(command = "echo 'Hello World'") {
                if (running.value) return;
                running.value = true;
                runLog.value += `[本地] 执行命令: ${command}...\n`;

                try {
                    socket = new WebSocket(`ws://${location.host}/ws/local-command`);
                    socket.onopen = () => {
                        // 发送命令到后端
                        socket.send(JSON.stringify({ command }));
                    };
                    // 添加执行结果状态变量
                    let localSuccess = false;
                    socket.onmessage = (ev) => {
                        // 解析JSON格式消息
                        try {
                            const msg = JSON.parse(ev.data);
                            if (msg.type === "msg") {
                                runLog.value += msg.data;
                                if (autoScroll.value) {
                                    scrollToBottom();
                                }
                            } else if (msg.type === "exit") {
                                // 根据退出码判断是否成功
                                localSuccess = (msg.data === 0);
                                runLog.value += `[本地] 完成，退出码: ${msg.data}\n`;
                            }
                        } catch (e) {
                            // 如果不是JSON格式，按原样处理
                            runLog.value += ev.data;
                            if (autoScroll.value) {
                                scrollToBottom();
                            }
                        }
                    };
                    // 添加返回Promise以便等待执行结果
                    return new Promise((resolve) => {
                        socket.onclose = () => {
                            running.value = false;
                            // 使用结构化数据判断执行结果
                            if (localSuccess) {
                                Quasar.Notify.create({ type: 'positive', message: '本地命令执行完成' });
                            } else {
                                Quasar.Notify.create({ type: 'negative', message: '本地命令执行失败' });
                            }
                            resolve(localSuccess);
                        };
                    });
                } catch (e) {
                    running.value = false;
                    Quasar.Notify.create({ type: 'negative', message: '本地命令执行失败: ' + e.toString() });
                    return Promise.resolve(false); // 返回失败状态
                }
            }

            // 新增：一键运行功能
            async function executeAll() {
                runLog.value += "[一键运行] 开始执行...\n";
                // 等待编译执行结果
                const buildSuccess = await executeBuild();
                // 如果编译失败，则不继续执行运行步骤
                if (!buildSuccess) {
                    runLog.value += "[一键运行] 编译失败，终止执行...\n";
                    running.value = false;
                    return;
                }

                runLog.value += "[一键运行] 编译完成，开始运行...\n";
                // 等待运行执行结果
                const runSuccess = await executeRunCommand();
                // 显示最终执行结果
                if (runSuccess) {
                    runLog.value += "[一键运行] 所有步骤执行成功\n";
                } else {
                    runLog.value += "[一键运行] 运行步骤失败\n";
                }
            }


            // WebSocket 实时日志
            async function startRun() {
                if (running.value) return;
                running.value = true;
                runLog.value += "[运行] 开始执行...\n";

                // 组装命令：把镜像名放进去，可继续加端口映射等
                const cmd = `docker run --rm -t ${selectedImage.value}`;

                socket = new WebSocket(`ws://${location.host}/ws/run`);
                socket.onopen = () => {
                    // 连接成功后，把运行服务器配置+命令一次性发给后端
                    socket.send(JSON.stringify({
                        host: cfgRun.ip,
                        port: cfgRun.port,
                        user: cfgRun.user,
                        pwd: cfgRun.pwd,
                        cmd
                    }));
                };
                socket.onmessage = (ev) => {
                    // 解析JSON格式消息
                    try {
                        const msg = JSON.parse(ev.data);
                        if (msg.type === "msg") {
                            runLog.value += msg.data;
                            if (autoScroll.value) {
                                scrollToBottom();
                            }
                        } else if (msg.type === "exit") {
                            runLog.value += `\n[EXIT CODE] ${msg.data}\n`;
                        }
                    } catch (e) {
                        // 如果不是JSON格式，按原样处理
                        runLog.value += ev.data;
                        if (autoScroll.value) {
                            scrollToBottom();
                        }
                    }
                };
                socket.onclose = () => (running.value = false);
            }

            function stopRun() {
                socket && socket.close();
                running.value = false;
                buildRunning.value = false;
                runCommandRunning.value = false;
            }

            return {
                tab,
                cfgBuild, cfgRun,
                images, selectedImage, loadingImages, listReleaseVersions, listReleaseSnapshotVersions,
                selectedMindieVersion, // 添加这一行
                runLog, running, buildRunning, runCommandRunning,
                testConn, startRun, stopRun,
                saveConfig,  // 暴露保存配置函数
                executeBuild, // 新增编译函数
                executeRunCommand, // 新增运行函数
                executeAll, // 新增一键运行函数
                autoScroll, toggleAutoScroll, // 自动滚动相关
                logArea, // 日志区域引用
                clearLog, // 添加清空日志函数
                reloadMindIEVersion,
                openArtgetConfig,
                addSelectedMindieVersion, // 添加这一行
                removeSelectedMindieVersion, // 添加这一行
                selectedGpus, // 添加GPU选择引用
                saveGpuConfig, // 添加保存GPU配置函数
                checkGpuStatus, // 添加查看GPU状态函数
                executeCompileCommand, // 添加编译命令执行函数
                executeLocalCommand // 添加本地命令执行函数
            };
        }
    });

    app.use(Quasar);
    app.mount('#q-app');
</script>

</html>